=begin rdoc
Unit tests for event class.

Author::    Matthias Woehrle  (mailto:matthias.woehrle@tik.ee.ethz.ch)
Copyright:: Copyright (c) 2008 ETH Zurich
License::   

TODO:

MISSING:
=end

$:.unshift File.join(File.dirname(__FILE__), "..", "lib")

require 'Event'
require 'test/unit'

class TestEvent < Test::Unit::TestCase
  
  def setup
    @e = Event.new(:nodeid => 3, :type => :send, :origin=> 0)
    @f = Event.new(:nodeid => 3, :type => :send)

  end

  #simple initialization test - checking key-value pairs
  def test_init_should_return_an_object_with_nodeid_and_type_and_return_the_correct_values_for_the_keys
    e = Event.new(:nodeid => 3, :type => :send, :origin=> 0)
    assert_equal(e.has_key?(:nodeid), true)
    assert_equal(e.has_key?(:type), true)
    assert_equal(e[:nodeid],3)
    assert_equal(e[:type],:send)
    assert_equal(e[:origin],0)
  end

#  #check events cannot be created with non-symbol keys (int)
#  def test_format_exception_init_should_raise_exception_as_last_key_is_an_int
#    assert_raise Exception do
#      newevent = Event.new(:nodeid => 3, :type => :send, 2 => 12)
#    end
#  end
#
#
#  #check events cannot be created with non-symbol keys (string)
#  def test_format_exception_init_should_raise_exception_as_last_key_is_a_string
#    assert_raise Exception do
#      newevent = Event.new(:nodeid => 3, :type => :send, "2" => 12)
#    end
#  end
#
#  #check events cannot be assigned to with non-symbol keys (int)
#  def test_format_exception_assignment_should_raise_exception_as_key_is_an_int
#    dummy = Event.new(:nodeid => "xyz", :type => :send)
#    assert_raise Exception do
#      dummy[2] = 12
#    end
#  end
#
#
#  #check events cannot be assigned to with non-symbol keys (string)
#  def test_format_exception_assignment_should_raise_exception_as_key_is_a_string
#    dummy = Event.new(:nodeid => "xyz", :type => :send)
#    assert_raise Exception do
#      dummy["2"] = 12
#    end
#  end
#
#  #Events need to have a nodeid
#  def test_format_exception_nodeid_should_raise_exception_as_nodeid_is_missing
#    assert_raise Exception do
#      newevent = Event.new(:type => :send, :origin=> 0)
#    end
#  end
#
#  #Events need to have a type
#  def test_format_exception_type_should_raise_exception_as_type_is_missing
#    assert_raise Exception do
#      newevent = Event.new(:nodeid => 3, :origin=> 0)
#    end
#  end
  
  #check that appending of keys works
  def test_append_should_return_the_values_for_newly_appended_keys
    appender = Event.new(:nodeid => 3, :type => :send, :origin=> 0)
    appender[:time] = 124
    appender[:extrafield] = 'dummy'
    assert_equal(appender[:extrafield],'dummy')
    assert_equal(appender[:time],124)
  end

  #check that nodeid with string works
  def test_append_should_return_a_string_for_the_nodeid
    string_nodeid = Event.new(:nodeid => "xyz", :type => :send)
    assert_equal(string_nodeid[:nodeid],'xyz')
  end
  
  # check that adding results in same results as with initialization
  def test_event_compare_should_return_that_the_two_events_are_the_same
    long = Event.new(:nodeid => 3, :type => :send, :origin=> 0)
    short = Event.new(:nodeid => 3, :type => :send)
    
    short[:origin] = 0
    
    assert_equal(long,short)
  end

  # check puts_s method (it uses sort for the keys)
  def test_to_string_and_should_return_a_format_string_with_sorted_keys
    stringForE = "Event: nodeid : 3; origin : 0; type : send;"
    assert(@e.to_s.eql?(stringForE))
  end

  #check ordering less than
  def test_larger_should_return_that_event_e_is_smaller_because_of_nodeid
     h = Event.new(:nodeid => 4, :type => :receive, :origin=> 2)     
     assert_equal(-1,@e<=>h)
  end

  #check ordering bigger than  
  def test_smaller_should_return_that_event_e_is_bigger_because_of_nodeid
    h = Event.new(:nodeid => 2, :type => :receive, :origin=> 2)    
    assert_equal(1, @e<=>h)
  end

  #check bigger than based on secondary argument
  def test_larger_should_return_that_event_e_is_smaller_because_of_type
    h = Event.new(:nodeid => 3, :type => :transmit, :origin=> 1)    
    assert_equal(-1, @e<=>h)
  end

  #check less than based on secondary argument
  def test_larger_should_return_that_event_e_is_bigger_because_of_type
    h = Event.new(:nodeid => 3, :type => :receive, :origin=> 1)    
    assert_equal(1, @e<=>h)
  end

  #check equality which yields 0
  def test_equal_should_return_eqaulity_as_events_are_equal_except_origin_which_is_not_evaluated
    h = Event.new(:nodeid => 3, :type => :send, :origin=> 0)
    assert_equal(0, @e<=>h)
  end
  


end

class Tester1Event < Event; @type = :testerEvent;  def initialize(*args); super([:testerEvent].concat(*args)); end; end


class TestEventEquality < Test::Unit::TestCase
  def setup
    Tester1Event.with(:name =>:nodeid, :fieldtype=> :integer)    
  end
  
  #check that init with an array after defining a suitable event type works
  def test_init_with_array_for_defined_event_type_should_return_new_event_with_given_properties
    a = Tester1Event.new([12])
    b = Event.new(:nodeid=>12, :type=>:testerEvent)
    assert_equal(a, b)
  end
  
  #check ==
  def test_equal_should_return_true_although_different_types_of_events
    a = Tester1Event.new([12])
    b = {:nodeid=>12, :type=>:testerEvent}
    assert(b==a)
  end
    
  #check event equality
  def test_equal_should_return_false_because_different_types_of_events   
    a = Tester1Event.new([12])
    b = {:nodeid=>12, :type=>:testerEvent}
    assert(!b.eql?(a))
  end
  
  #check event inequality
  def test_equal_should_return_false_because_different_types_of_events   
    a = Tester1Event.new([12])
    b = 12
    assert(!b.eql?(a))
  end  
  
  #check event inequality
  def test_equal_should_return_false_because_different_types_of_events   
    a = Tester1Event.new([12])
    b = 12
    assert(!a.eql?(b))
  end
end

class Tester2Event < Event; @type = :testerEvent;  def initialize(*args); super([:testerEvent].concat(*args)); end; end
class TestEventClassMethods < Test::Unit::TestCase
    
  def test_event_subclassing_should_return_that_allback_set_constraints_and_validkeys
        assert_equal([:type], Tester2Event.instance_eval{@validkeys})
        assert_equal(Hash.new, Tester2Event.instance_eval{@constraints})        
  end
  
  def test_self_with_should_return_full_blown_constraint
    Tester2Event.with(:name =>:seqNo, :fieldtype=> :periodic, :range => 0..255, :notification => :error)
    constraint = {:fieldtype=> :periodic, :range => 0..255, :notification => :error}
    assert_equal(constraint, Tester2Event.instance_eval{@constraints[:seqNo]})
  end
  
  def test_self_with_should_return_constraint_filled_up_with_default_values
    Tester2Event.with(:name =>:time, :fieldtype=> :integer)
    constraint = {:fieldtype=> :integer}
    assert_equal(constraint, Tester2Event.instance_eval{@constraints[:time]})
  end

  def test_self_with_without_name_should_raise_runtime_error
      assert_raise RuntimeError do
        Tester2Event.with(:fieldtype=> :integer)
      end
   end
   
end

class Tester3Event < Event; @type = :tester3Event;  def initialize(*args); super([:tester3Event].concat(*args)); end; end
class Tester3PEvent < Event; @type = :tester3PEvent;  def initialize(*args); super([:tester3PEvent].concat(*args)); end; end

class TestEventNotification < Test::Unit::TestCase
  
  #test that the default of notification is a warning which does not raise an exception.
  def test_constraint_violation_should_assert_warning_due_to_wrong_nodeid_albeit_no_notification_specified
    Tester3Event.with(:name =>:nodeid, :fieldtype=> :integer)        
    Tester3Event.with(:name =>:seqNo, :fieldtype=> :integer, :range => 0..255, :notification => :error)    
    assert_nothing_raised do
      Tester3Event.new([1, 0])
    end
  end
  
  #test that notification of tyoe error raise a runtime exception.
  def test_constraint_violation_should_assert_warning_due_to_seqNo_out_of_bounds
    Tester3PEvent.with(:name =>:nodeid, :fieldtype=> :integer)    
    Tester3PEvent.with(:name =>:seqNo, :fieldtype=> :periodic, :range => 0..255, :notification => :error)
    assert_raise RuntimeError do
      Tester3PEvent.new([17,2561])
    end
  end

  #test that false notification raises runtime error
  def test_specfication_violation_should_raise_runtime_error
    assert_raise RuntimeError do
        Tester3Event.with(:name =>:nodeid, :fieldtype=> :integer)              
        Tester3PEvent.with(:name =>:seqNo, :fieldtype=> :periodic, :range => 0..255, :notification => :something_else)
        Tester3PEvent.new([11, 1222])
    end
  end

  #test that notification of tyoe error raise a runtime exception.
  def test_constraint_violation_on_integer_should_raise_warning
      assert_nothing_raised do
        Tester3Event.with(:name =>:nodeid, :fieldtype=> :integer, :range => [0,100,20534476834])
        Tester3Event.new([88])
      end
  end
  #test that unsupported fieldtype causes exception
  def test_unsupported_fieldtype
    assert_raise RuntimeError do
        Tester3Event.with(:name =>:nodeid, :fieldtype=> :event, :range => [0,100,20534476834])
        Tester3Event.new([Event.new(:nodeid => 3, :type => :send, :origin=> 2)])
      end
  end

  #test that unsupported constraint type causes exception
  def test_unsupported_constraint_type
    assert_raise RuntimeError do
        Tester3Event.with(:name =>:nodeid, :fieldtype=> :integer, :interval => 0)
        Tester3Event.new([88])
      end
  end

  #test that unsupported constraint type causes exception
  def test_unsupported_constraint_type
    require 'set'
    b = Set.new
    b << 1 << 2 <<3 
    assert_raise RuntimeError do
        Tester3Event.with(:name =>:nodeid, :fieldtype=> :integer, :range => b)
        Tester3Event.new([88])
      end
  end


end